/**
 * Alipay.com Inc.
 * Copyright (c) 2005-2008 All Rights Reserved.
 */
package com.tiancikeji.zaoke.app.service.bill.impl.alipay.util;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.tiancikeji.zaoke.app.model.order.Bill;
import com.tiancikeji.zaoke.app.service.bill.impl.alipay.AlipayWapConfig;
import com.tiancikeji.zaoke.app.service.bill.impl.alipay.vo.DirectTradeCreateRes;
import com.tiancikeji.zaoke.app.service.bill.impl.alipay.vo.ErrorCode;
import com.tiancikeji.zaoke.app.service.bill.impl.alipay.vo.ResponseResult;

/**
 * 
 * 调用支付宝的开放平台创建、支付交易步骤
 * 
 * 1.将业务参数：外部交易号、商品名称、商品总价、卖家帐户、卖家帐户、notify_url这些东西按照xml 的格式放入<req_data></req_data>中 2.将通用参数也放入请求参数中
 * 3.对以上的参数进行签名，签名结果也放入请求参数中 4.请求支付宝开放平台的alipay.wap.trade.create.direct服务
 * 5.从开放平台返回的内容中取出request_token（对返回的内容要先用私钥解密，再用支付宝的公钥验签名） 6.使用拿到的request_token组装alipay.wap.auth.authAndExecute服务的跳转url
 * 7.根据组装出来的url跳转到支付宝的开放平台页面，交易创建和支付在支付宝的页面上完成
 * 
 */
public class Md5Trade {

    private AlipayWapConfig alipayWapConfig;

    private static final Log payBackLogger = LogFactory.getLog("payBack");

    private SecurityManager securityManager = new SecurityManagerImpl();

    public String getPayUrl(Bill bill, String system) throws IOException {

        // 使用serialId而不是outSerialId ！！！
        String outTradeNo = bill.getSerialId();
        String totalFee = String.valueOf(bill.getMoney());
        String subject = bill.getSubject();
        String code = bill.getCode();
        Map<String, String> reqParams = prepareTradeRequestParamsMap(outTradeNo, totalFee, subject, code, system);

        String signAlgo = alipayWapConfig.getSec_id().trim();
        String key = alipayWapConfig.getKey().trim();
        String reqUrl = alipayWapConfig.getReq_url().trim();

        String sign = sign(reqParams, signAlgo, key);

        reqParams.put("sign", sign);
        /*
         * String encryptedReqData = ""; encryptedReqData = encryptReqData(reqParams.get("req_data"));
         * reqParams.put("req_data", encryptedReqData);
         */
        ResponseResult resResult = new ResponseResult();
        String businessResult = "";

        try {
            resResult = send(reqParams, reqUrl, signAlgo, key);
        }
        catch (Exception e1) {
            e1.printStackTrace();
            String errStr = "alipay.wap.trade.create.direct出现异常:" + e1.getMessage();
            payBackLogger.error(errStr);
            throw new RuntimeException(errStr);
        }
        if (resResult != null && resResult.isSuccess()) {
            businessResult = resResult.getBusinessResult();
            String errStr = "业务成功结果:" + businessResult;
            payBackLogger.error(errStr);
            System.out.println(errStr);
        }
        else {
            String errStr = "alipay.wap.trade.create.direct服务返回失败";
            payBackLogger.error(errStr);
            System.out.println(errStr);
            throw new RuntimeException(errStr);
        }
        DirectTradeCreateRes directTradeCreateRes = null;
        XMapUtil.register(DirectTradeCreateRes.class);

        if (StringUtils.isBlank(businessResult)) {
            String errStr = "alipay.wap.trade.create.direct服务返回参数有问题";
            payBackLogger.error(errStr);
            throw new RuntimeException(errStr);
        }

        try {
            directTradeCreateRes = (DirectTradeCreateRes) XMapUtil.load(new ByteArrayInputStream(businessResult
                    .getBytes(alipayWapConfig.getEncoding())));
        }
        catch (Exception e) {
            String errStr = "解析alipay.wap.trade.create.direct服务返回出现异常";
            payBackLogger.error(errStr);
            throw new RuntimeException(errStr);
        }

        if (directTradeCreateRes == null) {
            String errStr = "解析alipay.wap.trade.create.direct服务返回的<direct_trade_create_res>为null";
            payBackLogger.error(errStr);
            throw new RuntimeException(errStr);
        }

        // 开放平台返回的内容中取出request_token
        String requestToken = directTradeCreateRes.getRequestToken();
        if (StringUtils.isBlank(requestToken)) {
            String errStr = "解析alipay.wap.trade.create.direct服务返回的request_token为null";
            payBackLogger.error(errStr);
            throw new RuntimeException(errStr);
        }

        Map<String, String> authParams = prepareAuthParamsMap(requestToken, outTradeNo, system);
        String authSign = sign(authParams, signAlgo, key);
        authParams.put("sign", authSign);
        String redirectURL = "";
        try {
            redirectURL = getRedirectUrl(authParams, reqUrl);
            String errStr = "跳转地址:" + redirectURL;
            payBackLogger.error(errStr);
            System.out.println(errStr);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        if (StringUtils.isNotBlank(redirectURL)) {
            return redirectURL;
        }
        else {
            String errStr = "获取跳转地址失败";
            payBackLogger.error(errStr);
            throw new RuntimeException(errStr);
        }
    }

    /**
     * 准备alipay.wap.trade.create.direct服务的参数
     * 
     * @param request
     * @return
     * @throws UnsupportedEncodingException
     */
    private Map<String, String> prepareTradeRequestParamsMap(String outTradeNo, String totalFee, String subject, String code, String system)
            throws UnsupportedEncodingException {
        Map<String, String> requestParams = new HashMap<String, String>();

        String sellerAccountName = alipayWapConfig.getSeller_account_name().trim();
        String notifyUrl = alipayWapConfig.getNotify_url().trim();
        String merchantUrl = alipayWapConfig.getMerchant_url().trim();

        String reqData = "<direct_trade_create_req>" + "<subject>" + subject + "</subject><out_trade_no>" + outTradeNo
                + "</out_trade_no><total_fee>" + totalFee + "</total_fee><seller_account_name>" + sellerAccountName
                + "</seller_account_name><notify_url>" + notifyUrl + "</notify_url><merchant_url>" + merchantUrl
                + "</merchant_url>";
        reqData += "</direct_trade_create_req>";
        requestParams.put("req_data", reqData);
        requestParams.put("req_id", System.currentTimeMillis() + "");
        requestParams.putAll(prepareCommonParams(outTradeNo, system));
        System.out.println("prepareTradeRequestParamsMap" + requestParams);
        return requestParams;
    }

    /**
     * 准备alipay.wap.auth.authAndExecute服务的参数
     * 
     * @param request
     * @param requestToken
     * @return
     */
    private Map<String, String> prepareAuthParamsMap(String requestToken, String outTradeNo, String system) {
        Map<String, String> requestParams = new HashMap<String, String>();
        String reqData = "<auth_and_execute_req><request_token>" + requestToken
                + "</request_token></auth_and_execute_req>";
        requestParams.put("req_data", reqData);
        requestParams.putAll(prepareCommonParams(outTradeNo, system));
//        String callbackUrl = alipayWapConfig.getCall_back_url().trim();
//        callbackUrl += "?serialId=" + (StringUtils.isBlank(outTradeNo) ? "" : outTradeNo);
//        requestParams.put("call_back_url", callbackUrl);
        requestParams.put("service", "alipay.wap.auth.authAndExecute");
        return requestParams;
    }

    /**
     * 准备通用参数
     * 
     * @param request
     * @return
     */
    private Map<String, String> prepareCommonParams(String outTradeNo, String system) {
        Map<String, String> commonParams = new HashMap<String, String>();

        String service = alipayWapConfig.getService_create();
        commonParams.put("service", service);

        String secId = alipayWapConfig.getSec_id();
        commonParams.put("sec_id", secId);

        String partner = alipayWapConfig.getPartner_id();
        commonParams.put("partner", partner);

        String callbackUrl = alipayWapConfig.getCall_back_url().trim();
        callbackUrl += "?serialId=" + (StringUtils.isBlank(outTradeNo) ? "" : outTradeNo);
        if(StringUtils.isNotBlank(system))
        	callbackUrl += "&system=" + system;
        commonParams.put("call_back_url", callbackUrl);

        String format = alipayWapConfig.getFormat();
        commonParams.put("format", format);

        String v = alipayWapConfig.getVersion();
        commonParams.put("v", v);

        System.out.println("prepareCommonParams" + commonParams);
        return commonParams;
    }

    /**
     * 对参数进行签名
     * 
     * @param reqParams
     * @return
     */
    private String sign(Map<String, String> reqParams, String signAlgo, String key) {

        String signData = ParameterUtil.getSignData(reqParams);
        String sign = "";
        try {
            sign = securityManager.sign(signAlgo, signData, key);
        }
        catch (Exception e1) {
            e1.printStackTrace();
        }
        return sign;
    }

    /**
     * 调用alipay.wap.auth.authAndExecute服务的时候需要跳转到支付宝的页面，组装跳转url
     * 
     * @param reqParams
     * @return
     * @throws Exception
     */
    private String getRedirectUrl(Map<String, String> reqParams, String reqUrl) throws Exception {
        String redirectUrl = reqUrl + "?";
        redirectUrl = redirectUrl + ParameterUtil.mapToUrl(reqParams);
        return redirectUrl;
    }

    /**
     * 调用支付宝开放平台的服务
     * 
     * @param reqParams
     * @return
     * @throws Exception
     */
    private ResponseResult send(Map<String, String> reqParams, String reqUrl, String secId, String key)
            throws Exception {
        String response = "";
        String invokeUrl = reqUrl + "?";
        URL serverUrl = new URL(invokeUrl);
        HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection();

        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        conn.connect();
        String params = ParameterUtil.mapToUrl(reqParams);
        payBackLogger.error("请求服务alipay.wap.trade.create.direct：" + params);
        conn.getOutputStream().write(params.getBytes());

        InputStream is = conn.getInputStream();

        BufferedReader in = new BufferedReader(new InputStreamReader(is));
        StringBuffer buffer = new StringBuffer();
        String line = "";
        while ((line = in.readLine()) != null) {
            buffer.append(line);
        }

        response = URLDecoder.decode(buffer.toString(), alipayWapConfig.getEncoding());
        payBackLogger.error("服务alipay.wap.trade.create.direct返回：" + buffer.toString());
        payBackLogger.error("服务alipay.wap.trade.create.direct返回（url解码后）：" + response);
        conn.disconnect();
        return praseResult(response, secId, key);
    }

    /**
     * 解析支付宝返回的结果
     * 
     * @param response
     * @return
     * @throws Exception
     */
    private ResponseResult praseResult(String response, String secId, String key) throws Exception {
        // ???ó??
        HashMap<String, String> resMap = new HashMap<String, String>();
        String v = ParameterUtil.getParameter(response, "v");
        String service = ParameterUtil.getParameter(response, "service");
        String partner = ParameterUtil.getParameter(response, "partner");
        // String secId = ParameterUtil.getParameter(response, "sec_id");
        String sign = ParameterUtil.getParameter(response, "sign");
        String reqId = ParameterUtil.getParameter(response, "req_id");
        resMap.put("v", v);
        resMap.put("service", service);
        resMap.put("partner", partner);
        resMap.put("sec_id", secId);
        resMap.put("req_id", reqId);
        String businessResult = "";
        ResponseResult result = new ResponseResult();
        if (response.contains("<err>")) {
            result.setSuccess(false);
            businessResult = ParameterUtil.getParameter(response, "res_error");

            XMapUtil.register(ErrorCode.class);
            ErrorCode errorCode = (ErrorCode) XMapUtil.load(new ByteArrayInputStream(businessResult
                    .getBytes(alipayWapConfig.getEncoding())));
            result.setErrorMessage(errorCode);

            resMap.put("res_error", ParameterUtil.getParameter(response, "res_error"));
        }
        else {
            businessResult = ParameterUtil.getParameter(response, "res_data");
            result.setSuccess(true);
            result.setBusinessResult(businessResult);
            resMap.put("res_data", businessResult);
        }
        String verifyData = ParameterUtil.getSignData(resMap);

        System.out.println(verifyData);
        boolean verified = securityManager.verify(secId, verifyData, sign, key);

        if (!verified) {
            throw new Exception("验证签名失败");
        }
        return result;
    }

	public AlipayWapConfig getAlipayWapConfig() {
		return alipayWapConfig;
	}

	public void setAlipayWapConfig(AlipayWapConfig alipayWapConfig) {
		this.alipayWapConfig = alipayWapConfig;
	}

}
